﻿// AddFileListCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "iTrans.h"
#include "AddFileListCtrl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

/////////////////////////////////////////////////////////////////////////////
// CAddFileListCtrl

// 어떠한 VS에서도 정의되지 않은 값이라니...
#ifndef WM_COPYGLOBALDATA
#define WM_COPYGLOBALDATA 0x0049
#endif

// 아래의 CHANGEWINDOWMESSAGEFILTER와 MSGFLT_ADD는
// tergetver.h에서 XP 전용으로 맞추면 없는 값이 되어버림
#ifndef CHANGEWINDOWMESSAGEFILTER
typedef BOOL (WINAPI *CHANGEWINDOWMESSAGEFILTER)(UINT message, DWORD dwFlag);
#endif
#ifndef MSGFLT_ADD
#define MSGFLT_ADD 1
#endif

CAddFileListCtrl::CAddFileListCtrl()
{
	cbRemoveAll = NULL;

	HINSTANCE hDll;                
	hDll = LoadLibrary(_T("USER32.DLL"));
	if (hDll) {
		CHANGEWINDOWMESSAGEFILTER ChangeWindowMessageFilter_
			=(CHANGEWINDOWMESSAGEFILTER)GetProcAddress(hDll,"ChangeWindowMessageFilter");
		if (ChangeWindowMessageFilter_) {
			ChangeWindowMessageFilter_(WM_DROPFILES, MSGFLT_ADD);
			ChangeWindowMessageFilter_(WM_COPYDATA, MSGFLT_ADD);
			ChangeWindowMessageFilter_(WM_COPYGLOBALDATA, MSGFLT_ADD);
		}
		FreeLibrary(hDll);
	}
}

CAddFileListCtrl::~CAddFileListCtrl() {}


BEGIN_MESSAGE_MAP(CAddFileListCtrl, CListCtrl)
	//{{AFX_MSG_MAP(CAddFileListCtrl)
	ON_WM_DROPFILES()
	//}}AFX_MSG_MAP
	ON_NOTIFY_REFLECT(NM_DBLCLK, &CAddFileListCtrl::OnNMDblclk)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAddFileListCtrl message handlers

//////////////////////////////////////////////////////////////////
//	use IShellLink to expand the shortcut
//	returns the expanded file, or "" on error
//
//	original code was part of CShortcut
//	1996 by Rob Warner
//	rhwarner@southeast.net
//	http://users.southeast.net/~rhwarner

CString CAddFileListCtrl::ExpandShortcut(CString &inFile)
{
	CString outFile;

	// Make sure we have a path
	if (inFile.IsEmpty()) return outFile;

	IShellLink* psl;
	HRESULT hres;
	LPTSTR lpsz = const_cast<LPTSTR>(static_cast<LPCTSTR>(inFile));
	CT2W wsz(lpsz);

	CoInitialize ( NULL );
	// Create instance for shell link
	hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
	                          IID_IShellLink, (LPVOID*) &psl);
	if (SUCCEEDED(hres)) {
		// Get a pointer to the persist file interface
		IPersistFile* ppf;
		hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*) &ppf);
		if (SUCCEEDED(hres)) {
			// Load shortcut
			hres = ppf->Load(wsz, STGM_READ);
			if (SUCCEEDED(hres)) {
				WIN32_FIND_DATA wfd;
				// find the path from that
				/*HRESULT hres = */psl->GetPath(outFile.GetBuffer(MAX_PATH),
				                            MAX_PATH,
				                            &wfd,
				                            SLGP_UNCPRIORITY);

				outFile.ReleaseBuffer();
			}
			ppf->Release();
		}
		psl->Release();
	}
	CoUninitialize();

	// if this fails, outFile == ""
	return outFile;
}

//
//	handle WM_DROPFILES
//

void CAddFileListCtrl::OnDropFiles(HDROP dropInfo)
{
	// Get the number of pathnames that have been dropped
	WORD wNumFilesDropped = DragQueryFileW(dropInfo, -1, NULL, 0);

	int iNoneASCII = 0;

	// get all file names. but we'll only need the first one.
	for (WORD x = 0 ; x < wNumFilesDropped; x++) {

		// Get the number of bytes required by the file's full pathname
		WORD wPathnameSize = DragQueryFileW(dropInfo, x, NULL, 0)+1;

		// Allocate memory to contain full pathname & zero byte
		TCHAR * npszFile = new TCHAR[wPathnameSize];

		// If not enough memory, skip this one
		if (npszFile == NULL) continue;

		ZeroMemory(npszFile, sizeof(TCHAR)*wPathnameSize);

		// Copy the pathname into the buffer
		DragQueryFileW(dropInfo, x, npszFile, wPathnameSize);

		CString csName(npszFile);
		CString csExpanded = ExpandShortcut(csName);

		if (csExpanded.GetLength()) csName = csExpanded;

		// check and add to list
		// but now...
		if (FindFilename(csName)<0) {
			if (bIsASCII(csName)) {
				AddTailFilename(csName);
				addCSAFilename(static_cast<LPCTSTR>(csName));
			}
			else
				iNoneASCII++;
		}

		// clean up
		delete []npszFile;
	}

	if (iNoneASCII) {
		CString cs;
		cs.Format(_T("%d개의 파일명이 비 ASCII 문자 포함!"), iNoneASCII);
		MessageBox(cs);
	}

	if (cbRemoveAll && GetItemCount()) {
		cbRemoveAll->EnableWindow();
	}
	// Free the memory block containing the dropped-file information
	DragFinish(dropInfo);
}

int CAddFileListCtrl::FindFilename(CString csFilename)
{
	int iSlash = csFilename.ReverseFind(_TCHAR('\\'));
	int iDot = csFilename.ReverseFind(_TCHAR('.'));
	if (iSlash < iDot) {
		CString csExt(csFilename);
		csExt.Delete(0, iDot+1);
		csExt.MakeLower();

		static CString csExtList(_T(";avi;divx;mkv;flv;ogm;ogg;mp4;mov;qt;3gp;swf;wmv;asf;mpg;mpeg;dat;vob;m2ts;ts;ps;webm;rm;rmvb;ra;"));

		if (csExtList.Find(_T(";")+csExt+_T(";")) < 0) return 99999;
	} else
		return 99999;

	CString cs1, cs2;
	for (int i=0; i<this->GetItemCount(); i++) {
		cs1 = this->GetItemText(i, 0);
		cs2 = this->GetItemText(i, 1);

		if (csFilename.CompareNoCase(cs2+cs1) == 0) return i;
	}

	return -1;
}

void CAddFileListCtrl::AddTailFilename(CString csFilename)
{
	int iSlash = csFilename.ReverseFind(_TCHAR('\\'));
	CString csFolder(csFilename);

	csFilename.Delete(0, iSlash+1);
	csFolder = csFolder.Left(iSlash+1);

	int iPos = this->GetItemCount();

	this->InsertItem(iPos, csFilename);
	this->SetItemText(iPos, 1, csFolder);

	CheckExternalFiles(iPos);
}

void CAddFileListCtrl::InitListCtrl()
{
	HWND hWnd = this->GetSafeHwnd();
	DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);
	if (((dwStyle & LVS_TYPEMASK) != LVS_REPORT) ||
	        !(dwStyle & LVS_SHOWSELALWAYS)) {
		SetWindowLong(
		    hWnd,
		    GWL_STYLE,
		    (dwStyle & ~ LVS_TYPEMASK) | LVS_REPORT | LVS_SHOWSELALWAYS
		);
	}

	// 혹시나 해서 삽입한 "공장초기화"
	this->DeleteAllItems();
	removeAllCSAFilename();

	LVCOLUMN col;
	col.mask = LVCF_SUBITEM;
	while (this->GetColumn(0, &col)) {
		this->DeleteColumn(0);
	}
	// 공장초기화 완료

	static LPCTSTR colNames[] = {
			_T("파일명"),	_T("경로"),		_T("외부오디오"),	_T("외부자막"),
			_T("외부커버"),	_T("외부챕터"),	_T("진행")
	};
	static short int colSize[] = {
			310,	195,	75,		60,
			60,		60,		195
	};

	for (int i=0; i<7; i++) {
		this->InsertColumn(i, colNames[i], (i<2)?LVCFMT_LEFT:LVCFMT_CENTER, colSize[i]);
	}

	this->SetExtendedStyle(LVS_EX_FULLROWSELECT);

	this->DragAcceptFiles();
}

void CAddFileListCtrl::RemoveSelected()
{
	int iCount = this->GetSelectedCount();
	if (!iCount) return;

	int *pLine = new int[iCount];
	POSITION pos;

	pos = this->GetFirstSelectedItemPosition();
	int i=0;

	while (pos) {
		pLine[i++] = this->GetNextSelectedItem(pos);
	}

	for (i=0; i<iCount; i++) {
		int linetodel = pLine[i]-i;	// -i는 이미 위에서 삭제한 라인 수는 빼는 것
		this->DeleteItem(linetodel);
		removeAtCSAFilename(linetodel);
	}
	
	delete []pLine;
}

bool CAddFileListCtrl::checkFilename(int type, CString &name, LPCTSTR ext, int &pos, int column)
{
	if ((type==1)?(CSmallUtilz4iTrans::IsFileorfolder(name+_T(".")+ext)):(csGetFirstFilename(name+_T("*.")+ext).GetLength())) {
		this->SetItemText(pos, column, ext);
		return true;
	}
	
	return false;
}

void CAddFileListCtrl::CheckExternalFiles(int iPos)
{
	CString csFilename, csFolder;
	csFilename = this->GetItemText(iPos, 0);
	csFolder = this->GetItemText(iPos, 1);

	int iExtLen = csFilename.GetLength()-csFilename.ReverseFind(_TCHAR('.'))-1;
	CString csName = csFolder + csFilename.Left(csFilename.GetLength()-iExtLen-1);

	// 외부 챕터 존재 여부 확인
	if (csFilename.Right(4).CompareNoCase(_T(".ogm")))
		checkFilename(1, csName, _T("ogm"), iPos, 5);

	//여기서 커버 존재 여부 확인
	static LPCTSTR cover[] = {
		_T("jpeg"),	_T("jpg"),	_T("png")
	};

	for (int i=0; i<(sizeof(cover)/sizeof(cover[0])); i++) {
		if (checkFilename(1, csName, cover[i], iPos, 4)) break;
	}

	//여기서 자막 존재 여부 확인
	static LPCTSTR exSubtitle[] = {
		_T("ttxt"),	_T("srt"),	_T("smi"),	_T("ass"),	_T("ssa")
	};
	for (int i=0; i<(sizeof(exSubtitle)/sizeof(exSubtitle[0])); i++) {
		if (checkFilename(1, csName, exSubtitle[i], iPos, 3)) break;
	}
	

	//외부 오디오 존재 여부도 확인
	static LPCTSTR exAudio[] = {
		_T("aac"),	_T("wav"),	_T("flac"),	_T("dts"),
		_T("ac3"),	_T("mp3"),	_T("mp2"),	_T("wma")
	};
	for (int i=0; i<(sizeof(exAudio)/sizeof(exAudio[0])); i++) {
		if (checkFilename(1, csName, exAudio[i], iPos, 2)) break;
	}

	CString csTemp;
	csTemp = this->GetItemText(iPos, 2);
	if (csTemp.IsEmpty()) {
		// 딱 맞는 외부 오디오가 없으면 아래부분 처리
		
		// 외부 오디오 존재 여부 확인 / delay 및 언어ID 포함
		for (int i=0; i<(sizeof(exAudio)/sizeof(exAudio[0])); i++) {
			if (checkFilename(2, csName, exAudio[i], iPos, 2)) break;
		}
	}

	csTemp = this->GetItemText(iPos, 3);
	if (csTemp.IsEmpty()) {
		// 딱 맞는 외부 자막이 없으면 아래부분 처리
		
		// 외부 자막 존재 여부 확인 / 언어ID 포함
		static LPCTSTR exSubtitle2[] = {
			_T("ttxt"),	_T("srt"),	_T("ass"),	_T("ssa")
		};
		for (int i=0; i<(sizeof(exSubtitle2)/sizeof(exSubtitle2[0])); i++) {
			if (checkFilename(2, csName, exSubtitle2[i], iPos, 3)) break;
		}
	}
}

void CAddFileListCtrl::SetButtonRemoveAll(CButton *cb)
{
	cbRemoveAll = cb;
}

bool CAddFileListCtrl::bIsASCII(LPCTSTR lpFilename)
{
	// 파일명/폴더명에 비 아스키 문자가 있는지 확인

	// 일단 무조건 8.3으로 변환하고 시작함
	// 8.3으로 변환한 뒤에도 비 ANSI 문자가 있으면 그 때 튕김
	// 이 경우는 답이 없음

	long     length = 0;
	TCHAR*   buffer = NULL;

	length = GetShortPathName(lpFilename, NULL, 0);
	if (length == 0) return FALSE;

	buffer = new TCHAR[length];
	length = GetShortPathName(lpFilename, buffer, length);

	CT2A cFilename(buffer);
	CA2T tFilename(cFilename);

	bool bRet = !wcscmp(buffer, tFilename);
	
	delete []buffer;
	return bRet;
}

CString CAddFileListCtrl::csGetFirstFilename(LPCTSTR lp)
{
	CFileFind finder;
	CString csRet(_T(""));

	BOOL bOk = finder.FindFile(lp);
	if (!bOk) return csRet;

	while (bOk) {
		bOk = finder.FindNextFile();

		if (finder.IsDots()) continue;
		if (finder.IsDirectory()) continue;

		csRet = finder.GetFilePath();
		break;
	}

	finder.Close();
	return csRet;
}


void CAddFileListCtrl::OnNMDblclk(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	
	int iPos = pNMItemActivate->iItem;
	if ((iPos>=0) && (iPos < this->GetItemCount())) {
		ShellExecute(NULL, _T("open"), _T("explorer.exe"), static_cast<LPCTSTR>(_T("/select,\"")+csaFileNames.GetAt(iPos)+_T("\"")), NULL, SW_NORMAL);
	}

	*pResult = 0;
}

void CAddFileListCtrl::addCSAFilename(LPCTSTR fileName)
{
	csaFileNames.Add(fileName);
}

void CAddFileListCtrl::removeAtCSAFilename(INT_PTR nIndex)
{
	if (nIndex < csaFileNames.GetCount()) csaFileNames.RemoveAt(nIndex);
}

void CAddFileListCtrl::removeAllCSAFilename()
{
	csaFileNames.RemoveAll();
}

void CAddFileListCtrl::setAtCSAFilename(INT_PTR nIndex, LPCTSTR fileName)
{
	if (nIndex < csaFileNames.GetCount()) csaFileNames.SetAt(nIndex, fileName);
}